﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace LightXMachine
{
    public class OcclusionBox
    {
        #region Statics
        public static BasicEffect drawingEffect;
        
        public static void InitializeStaticMembers(GraphicsDevice device)
        {
            //by making these variables static between objects,
            //we save time and memory
            drawingEffect = new BasicEffect(device);
            drawingEffect.TextureEnabled = false;
            drawingEffect.VertexColorEnabled = true;
            drawingEffect.LightingEnabled = false;

            PresentationParameters pp = device.PresentationParameters;
            Matrix proj = Matrix.CreateOrthographicOffCenter(0, pp.BackBufferWidth, pp.BackBufferHeight, 0, 1, 50);
            Matrix viewMatrix = Matrix.CreateLookAt(new Vector3(0, 0, 5), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
            drawingEffect.World = Matrix.Identity;
            drawingEffect.Projection = proj;
            drawingEffect.View = viewMatrix;
        }
        #endregion //Statics

        public Color color;
        public float angle;
        public Vector2 position;
        public Rectangle boundingBox;

        public VertexPositionColor[] vertices = null;
        public bool[] backFacing = new bool[] { true, true, true, true };
        public Dictionary<string, VertexPositionColor[]> shadowVertices;
        public int shadowVertexCount;
        public int vertexCount = 4;

        public OcclusionBox()
        {
            shadowVertices = new Dictionary<string, VertexPositionColor[]>();
        }

        public void UpdateVertexList()
        {
            if (angle == 0)
            {
                vertices = new VertexPositionColor[] {
                        new VertexPositionColor(new Vector3(boundingBox.Left, boundingBox.Top, 0), Color.Black),
                        new VertexPositionColor(new Vector3(boundingBox.Right, boundingBox.Top, 0), Color.Black),
                        new VertexPositionColor(new Vector3(boundingBox.Right, boundingBox.Bottom, 0), Color.Black),
                        new VertexPositionColor(new Vector3(boundingBox.Left, boundingBox.Bottom, 0), Color.Black)
                    };
            }
            else
            {
                vertices = new VertexPositionColor[] {
                        new VertexPositionColor(new Vector3(Flip(boundingBox.Left, boundingBox.Top), 0), Color.Black),
                        new VertexPositionColor(new Vector3(Flip(boundingBox.Right, boundingBox.Top), 0), Color.Black),
                        new VertexPositionColor(new Vector3(Flip(boundingBox.Right, boundingBox.Bottom), 0), Color.Black),
                        new VertexPositionColor(new Vector3(Flip(boundingBox.Left, boundingBox.Bottom), 0), Color.Black)
                    };
            }
        }

        public void DetermineShadow(LightSource light, Vector2 lightSource, Vector3 screenPos)
        {
            if (!light.lightIsStatic || !shadowVertices.ContainsKey(light.id))
            {
                VertexPositionColor[] sVertices = new VertexPositionColor[shadowVertexCount * 2];

                //find beginning and ending vertices which
                //belong to the shadow
                int startingIndex = 0;
                int endingIndex = 0;
                for (int i = 0; i < vertexCount; i++)
                {
                    int currentEdge = i;
                    int nextEdge = (i + 1) % vertexCount;

                    if (backFacing[currentEdge] && !backFacing[nextEdge])
                        endingIndex = nextEdge;

                    if (!backFacing[currentEdge] && backFacing[nextEdge])
                        startingIndex = nextEdge;
                }

                shadowVertexCount = 0;

                //nr of vertices that are in the shadow

                if (endingIndex > startingIndex)
                    shadowVertexCount = endingIndex - startingIndex + 1;
                else
                    shadowVertexCount = vertexCount + 1 - startingIndex + endingIndex;

                sVertices = new VertexPositionColor[shadowVertexCount * 2];

                //create a triangle strip that has the shape of the shadow

                //NEW VERSION
                //int currentIndex = endingIndex;
                //int svCount = 0;
                //Vector3 vertexPos;
                //Vector3 L2P;
                //Vector3 lightSourcePos = new Vector3(lightSource, 0);
                //Vector3 addedVecs = lightSourcePos - screenPos;
                //while (svCount != shadowVertexCount * 2)
                //{
                //    vertexPos = VertexList[currentIndex].Position;

                //    //one vertex on the hull
                //    sVertices[svCount] = new VertexPositionColor();
                //    sVertices[svCount].Color = color;
                //    sVertices[svCount].Position = vertexPos - screenPos;

                //    //one extruded by the light direction
                //    sVertices[svCount + 1] = new VertexPositionColor();
                //    sVertices[svCount + 1].Color = color;
                //    L2P = vertexPos - lightSourcePos;
                //    L2P.Normalize();
                //    sVertices[svCount + 1].Position = addedVecs + L2P * 9000;

                //    svCount += 2;
                //    currentIndex = (currentIndex + 1) % vertexCount;
                //}

                //OLD VERSION
                int currentIndex = endingIndex;
                int svCount = 0;
                while (svCount != shadowVertexCount * 2)
                {
                    Vector3 vertexPos = vertices[currentIndex].Position;

                    //one vertex on the hull
                    sVertices[svCount] = new VertexPositionColor();
                    sVertices[svCount].Color = color;
                    sVertices[svCount].Position = vertexPos;

                    //one extruded by the light direction
                    sVertices[svCount + 1] = new VertexPositionColor();
                    sVertices[svCount + 1].Color = color;
                    Vector3 L2P = vertexPos - new Vector3(lightSource, 0);
                    L2P.Normalize();
                    sVertices[svCount + 1].Position = new Vector3(lightSource, 0) + L2P * 9000;

                    svCount += 2;
                    currentIndex = (currentIndex + 1) % vertexCount;
                }
                shadowVertices[light.id] = sVertices;
            }
        }

        public VertexPositionColor[] TranslateShadows(string id, Vector3 screenPos)
        {
            VertexPositionColor[] updatedPos;
            VertexPositionColor[] oldPos;

            if (shadowVertices.TryGetValue(id, out oldPos))
            {
                updatedPos = new VertexPositionColor[oldPos.Length];
                for (int i = 0; i < updatedPos.Length; ++i)
                {
                    updatedPos[i].Position = oldPos[i].Position - screenPos;
                    updatedPos[i].Color = oldPos[i].Color;
                }

                return updatedPos;
            }

            return null;


        }

        private Vector2 Flip(int x, int y)
        {
            return Flip(new Vector2(x, y));
        }
        /// <summary>
        /// Translates a angled box' corner to it's actual position
        /// </summary>
        /// <param name="corner">The corner</param>
        /// <returns>The actual position.</returns>
        private Vector2 Flip(Vector2 corner)
        {
            if (angle != 0)
            {
                double temp = Math.PI - angle;
                corner.X -= boundingBox.Center.X;
                corner.Y -= boundingBox.Center.Y;

                double alfa = Math.Atan2(corner.Y, corner.X);
                float r = corner.Length();
                alfa -= temp;
                corner.X = (float)(r * Math.Cos(alfa));
                corner.Y = (float)(r * Math.Sin(alfa));

                corner.X += boundingBox.Center.X;
                corner.Y += boundingBox.Center.Y;
            }
            return corner;
        }

    }
}